import tag_parser
 

######################################################
# Data Structures:
####
freeAddress = 7

bucketLinkedListStart = -1000

yagProcedure = \
"(define Yag                                                     \
  (lambda fs                                                     \
    (let ((ms (map                                               \
        (lambda (fi)                                             \
          (lambda ms                                             \
            (apply fi (map (lambda (mi)                          \
                     (lambda args                                \
                       (apply (apply mi ms) args))) ms))))       \
                fs)))                                            \
      (apply (car ms) ms))))\n"

      
# constant table -  address in mem           structure in mem      stringRep
constantTable =   [
                     [        1,               ["T_VOID"]        ,    '~void'   ],
                     [        2,               ["T_NIL"]         ,    '()'      ],
                     [        3,               ["T_BOOL" , 0]    ,     '#f'     ],
                     [        5,               ["T_BOOL" , 1]    ,     '#t'     ]
                  ]


# constantLookup -   stringRep    index in constantTable
constantLookup = { 
                     '~void':        0,
                     '()':          1,
                     '#f':          2,
                     '#t':          3,  
                  }

                          

#globals table   -  stringRep                 address in mem
globalVarTable = {      
                   '+':                 [  "here will come the var address in mem"],
                   '-':                 [  "here will come the var address in mem"],
                   '>':                 [  "here will come the var address in mem"],
                   '<':                 [  "here will come the var address in mem"],
                   '=':                 [  "here will come the var address in mem"],
                   '*':                 [  "here will come the var address in mem"],
                   '/':                 [  "here will come the var address in mem"],
                   "BOOLEAN?":          [  "here will come the var address in mem"],
                   'NUMBER?':           [  "here will come the var address in mem"],
                   'INTEGER?':          [  "here will come the var address in mem"],
                   'PAIR?':             [  "here will come the var address in mem"],
                   'ZERO?':             [  "here will come the var address in mem"],
                   'PROCEDURE?':             [  "here will come the var address in mem"],
                   'INTEGER?':             [  "here will come the var address in mem"],
                   'CHAR?':             [  "here will come the var address in mem"],
                   'STRING?':             [  "here will come the var address in mem"],
                   'CDR':               [  "here will come the var address in mem"],
                   'CAR':               [  "here will come the var address in mem"],
                   'CONS':              [  "here will come the var address in mem"],
                   'SYMBOL->STRING':    [  "here will come the var address in mem"],
                   'STRING->SYMBOL':    [  "here will come the var address in mem"],
                   'MAKE-VECTOR':       [  "here will come the var address in mem"],
                   'VECTOR-LENGTH':       [  "here will come the var address in mem"],
                   'NULL?':             [  "here will come the var address in mem"],
                   'LIST':             [  "here will come the var address in mem"],
                   'EQ?':             [  "here will come the var address in mem"],
                   'SYMBOL?':             [  "here will come the var address in mem"],
		   'VECTOR?':             [  "here will come the var address in mem"],
		   'VECTOR':             [  "here will come the var address in mem"],
		   'APPLY':             [  "here will come the var address in mem"],
		   'MAP':             [  "here will come the var address in mem"],
		   'STRING-REF':             [  "here will come the var address in mem"],
		   'VECTOR-REF':             [  "here will come the var address in mem"],
		   'APPEND':             [  "here will come the var address in mem"],
		   'STRING-LENGTH':             [  "here will come the var address in mem"],
		   'INTEGER->CHAR':             [  "here will come the var address in mem"],
		   'CHAR->INTEGER':             [  "here will come the var address in mem"],
		   'MAKE-STRING':             [  "here will come the var address in mem"],
		   'REMAINDER':             [  "here will come the var address in mem"]
                   }

#Buckets LinkedList   -           structure in mem
#                      pointer to String      next Node in list    symbol name (for debug)
bucketLinkedList =  [
                    ]

preDefinedSym2Label = {
        '+':"PLUS",
	'*':"MULTI",
	'-':"MINUS",
	'/':"DIVIDE",
	'>':"GT",
	'<':"LT",
	'=':"NUM_EQ",
	"BOOLEAN?":"IS_BOOL",
	"STRING?":"IS_STRING",
	"CHAR?":"IS_CHAR",
	"NUMBER?":"IS_NUM",
	"INTEGER?":"IS_INT",
	"PAIR?":"IS_PAIR",
	"PROCEDURE?":"IS_PROC",
        "ZERO?":"IS_ZERO_SCHEME",
	"CDR":"CDR",
	"CAR":"CAR",
        "CONS":"CONS",
        "SYMBOL->STRING":"SYMBOL2STRING",
        "STRING->SYMBOL":"STRING2SYMBOL",
        "MAKE-VECTOR":"MAKEVECTOR",
	"NULL?":"IS_NULL",
	"LIST":"LIST",
	"EQ?":"EQ",
	"VECTOR?":"IS_VEC",
	"VECTOR":"VECTOR",
	"VECTOR-LENGTH":"VEC_LEN",
	"SYMBOL?":"IS_SYM",
	"APPLY":"APPLY",
	"MAP":"MAP",
	"INTEGER?":"IS_INT",
	"APPEND":"APPEND",
	"STRING-REF":"STRING_REF",
	"VECTOR-REF":"VECTOR_REF",
	"INTEGER->CHAR":"INTEGER_CHAR",
	"CHAR->INTEGER":"CHAR_INTEGER",
	"REMAINDER":"REMAINDER",
	"MAKE-STRING":"MAKE_STRING",
	"STRING-LENGTH":"STRING_LEN"
			}			
################################################################  


############################################################
#  debug utils
################################

def genMemArrayPrinting():
    code="\n\n /* printing of Hash Memory Array: */\n"
    for i in range(0,300):
        code = code + ("        print_mem(ADDR(IMM("+str(i)+")),"+str(i)+");\n")
    code = code + " /* printing of Hash Memory finished */\n"    
    return code
    
def genPrintMemProcedure():
    code = ""
    
    code = code + """        void print_mem(long int memory,long int mem_index){
        if(memory==937610)     { printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_VOID\");            getchar(); }
        else if(memory==722689){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_NIL\");             getchar(); }
        else if(memory==741553){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_BOOL\");            getchar(); }
        else if(memory==181048){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_CHAR\");            getchar(); }
        else if(memory==945311){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_INTEGER\");         getchar(); }
        else if(memory==799345){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_STRING\");          getchar(); }
        else if(memory==368031){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_SYMBOL\");          getchar(); }
        else if(memory==885397){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_PAIR\");            getchar(); }
        else if(memory==335728){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_VECTOR\");          getchar(); }
        else if(memory==276405){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_CLOSURE\");         getchar(); }
        else if(memory==451794){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_FRACTION\");        getchar(); }
        else if(memory==685967){ printf(\"mem[%ld]= %s\\n\", (mem_index),\"T_ENVVEC\");          getchar(); }
        else                   {printf(\"mem[%ld]= %ld\\n\", (mem_index),ADDR(IMM(mem_index)));  getchar(); }
        }"""
        
    return code

def genStackArrayPrinting():
    code="\n\n /* printing of Stack Array: */\n"
    for i in range(0,300):
        code = code + ("        print_stack(ADDR(IMM("+str(i)+")),"+str(i)+");\n")
    code = code + " /* printing of Stack finished */\n\n"    
    return code

def genPrintStackProcedure():
    code = ""
    
    code = code + """        void print_stack(long int memory,long int mem_index){
        if(memory==937610)     { printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_VOID\");            getchar(); }
        else if(memory==722689){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_NIL\");             getchar(); }
        else if(memory==741553){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_BOOL\");            getchar(); }
        else if(memory==181048){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_CHAR\");            getchar(); }
        else if(memory==945311){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_INTEGER\");         getchar(); }
        else if(memory==799345){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_STRING\");          getchar(); }
        else if(memory==368031){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_SYMBOL\");          getchar(); }
        else if(memory==885397){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_PAIR\");            getchar(); }
        else if(memory==335728){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_VECTOR\");          getchar(); }
        else if(memory==276405){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_CLOSURE\");         getchar(); }
        else if(memory==451794){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_FRACTION\");        getchar(); }
        else if(memory==685967){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_ENVVEC\");          getchar(); }
        else if(memory==777096){ printf(\"stack[%ld]= %s\\n\", (mem_index),\"T_FRAME_END\");       getchar(); }
        else                   {printf(\"stack[%ld]= %ld\\n\", (mem_index),ADDR(IMM(mem_index)));  getchar(); }
        }"""
        
    return code
#############################################################

    
def initAllDatabases():
    global freeAddress
    global constantLookup
    global constantTable
    constantLookup.clear()
    constantTable.clear()
    constantTable = [   [ 1, ["T_VOID"], '~void' ] , [ 2, ["T_NIL"], '()' ]  ,
                        [ 3, ["T_BOOL" , 0], '#t' ]  ,  [ 5, ["T_BOOL" , 1], '#f' ]    ]
    constantLookup = {  '~void': 0,   '()': 1,   '#f': 2,  '#t': 3   }
    freeAddress = 7

def genString2SymbolGlobalProcedure():
    global bucketLinkedListStart
    code = " /* global procedure - string->symbol */ \n"
    code = code + """
    STRING2SYMBOL:
      PUSH(FP);
      MOV(FP,SP);
      PUSH(R1);
      PUSH(R2);
  
      MOV(R0, FPARG(2));                 //R0 = sob string Pointer
      CMP(IND(R0), IMM(T_STRING));        // input check
      JUMP_NE(TYPE_ERROR);
      MOV(R2,IMM(""" + str(bucketLinkedListStart) + """));                           // R2 = bucketLinkedList start address (only useful to start the search) 
      MOV(R1,IND(R2));                          // R1 = pointer to first bucket at bucketList
      //notice that if there are no buckets, this address will pointer to SOB_NILL!
  
      STRING2SYMBOL_SEARCH_LOOP:
      CMP(IND(R1),T_NIL);                        // if bucketlist ended (last link point to nil)
      JUMP_EQ(STRING2SYMBOL_SEARCH_NOT_FOUND);        // then there was not an existing symbol already
      CMP(R0,INDD(R1,0));                        // if R1 bucket is pointing to R0 - the same sob string
      JUMP_EQ(STRING2SYMBOL_CREATE_SYM);            // then we found the string, its already has a symbol bucket
      MOV(R2,R1);                                // R2 = last checked bucket (saving this incase its the last bucket, we will need the one before the last)
      MOV(R1,INDD(R1,1));                        // R1 = pointer to next link in bucketList
      JUMP(STRING2SYMBOL_SEARCH_LOOP);
  
      STRING2SYMBOL_SEARCH_NOT_FOUND:            // we will create a new bucket, after last bucket

      PUSH(IMM(2));
      CALL(MALLOC);
      DROP(1);                                    // now R0 has the new bucket address!
      MOV(INDD(R2,1),R0);                        // link last bucket to the new one
      MOV(INDD(R0,0),FPARG(2));                    // R0[0] = pointer to sob string - given as argument!
      MOV(INDD(R0,1),IMM(2));                    // R0[0] = pointer to SOB_NIL
      MOV(R1,R0);                                // R1 = pointer to new bucket created
      
      STRING2SYMBOL_CREATE_SYM:                    // creating the new symbol, and link it to its bucket, which address is now in R1

      PUSH(IMM(2));
      CALL(MALLOC);
      DROP(1);                                    // now R0 has the new symbol address!
      MOV(INDD(R0,0),T_SYMBOL);                    // R0[0] = T_SYMBOL
      MOV(INDD(R0,1),R1);                        // R0[1] = pointer to symbol's bucket, which has the symbol string representation sob_string

      POP(R2);
      POP(R1);
      POP(FP);
      RETURN;                                    // return R0 = address to new SOB_SYMBOL 
      """
    code = code + " /* global procedure ended */ \n\n"
    return code
    
def constantTableMemSize():
    global constantTable
    lastConstantIndex = len(constantTable)-1
    lastConstantSize = constantTable[lastConstantIndex][1].__len__()
    return  constantTable[lastConstantIndex][0] + lastConstantSize - 1

def buildConstantTable(codeAsts):
    global constantLookup
    global constantTable
    global freeAddress
    constantGatherer = {}
    for ast in codeAsts:
        constantGatherer = ast.gatherConstants(constantGatherer) 
    constants = constantGatherer.values()
    nextAddress = constantTableMemSize() + 1       # initial constant table free address
    for const in constants:
        if not constantLookup.get(str(const)):          #only if there is not already an existing constant like this in constantTable! - in order to prevent duplication
            nextAddress = const.addToConstantTable(constantLookup,constantTable,nextAddress)
    freeAddress = nextAddress        


def genConstantTable():
    global constantTable
    code = " /* constant table: */\n"
    tableSize = constantTableMemSize()                          # constantTableMemSize() return the next free address, so table size is actualy - 1     
    code = code + "        PUSH(IMM(" + str(tableSize) + "));"    
    code = code + """    
        CALL(MALLOC);
        DROP(1);\n"""
    code = code + "        DECR(R0);                      // because loop already INC in the next move - which shouldnt be \n"    
    for const in constantTable:
        #adding type field:
        code = code + "        INCR(R0);\n"
        code = code + "        MOV(IND(R0),"+ const[1][0] + ");"
        ####debug:
        addressForDebug = const[0]
        stringRepForDebug = const[2]
        code = code + "        /* address:    " + str(addressForDebug) + "        stringRep:    \" " + str(stringRepForDebug) + " \" */"
        ####
        code = code + "\n"
        i = 1
        #adding rest of constants fields:
        while i<len(const[1]):
            code = code + "        INCR(R0);\n"
            code = code + "        MOV(IND(R0),IMM("+ str(const[1][i]) + "));"
            ####debug:
            addressForDebug = addressForDebug + 1
            code = code + "        // address:    " + str(addressForDebug)
            #####
            code = code + "\n"
            i = i + 1
    code = code + """/* constant table end */\n\n"""    
    return code


def buildNgenGlobalVarTable(codeAsts):
    global globalVarTable
    global freeAddress
    global preDefinedSym2Label
    SymNLabel = preDefinedSym2Label.items()
    # global procedures address in mem inititation:
    for item in SymNLabel:
        predefSym = item[0]    
        globalVarTable[str(predefSym)][0] = freeAddress
        freeAddress = freeAddress + 1
    #freeAddress =  freeAddress + len(globalVarTable)   
    # global variables gathering and generation:       
    for ast in codeAsts:
        freeAddress = ast.gatherGlobalVars(globalVarTable,freeAddress)
    code = "\n/* global variables table init: */      \n"
    tableSize = len(globalVarTable)
    code = code + "        PUSH(IMM("+ str(tableSize) +"""));    \t\t// 1 param = size of global variables table
        CALL(MALLOC);            \t\t\t//we only init the space - the data the variable will point to will only be created during runtime!
        DROP(1); \n"""
    # global procedures closures initiation:         
    for item in SymNLabel:
        predefSym = item[0]
        predefSymLabel = item[1]
        code = code + "        // global procedure:    "+ str(predefSym) + "\n"
#	code = code +"printf(\""+str(predefSymLabel)+"\\n\");"
        code = code + """        PUSH(LABEL(""" + str(predefSymLabel) + """));            \t// 2 param - closure body pointer
        PUSH(IMM(1));       \t\t// 1 param - env pointer
        CALL(MAKE_SOB_CLOSURE);
        DROP(2);
        """
        globalProcAddr = globalVarTable[str(predefSym)][0]
        code = code + "        MOV(IND(" + str(globalProcAddr) + "),R0); \n"
    code = code + "/* global variables table init finished */ \n"
    return code    
    
    
def buildBucketLinkedList(): 
    global bucketLinkedList
    global constantTable
    global constantLookup
    global freeAddress
    global bucketLinkedListStart
    # bucket list start - 1 size pointer to first bucket (if there is none, then to nil)
    bucketLinkedListStart = freeAddress
    freeAddress = freeAddress + 1                       # save one address for buckets list start
    newBucketAddr = freeAddress
    for const in constantTable:
        if const[1][0] == "T_SYMBOL":
            stringRepPointer = const[1][1]      # stringRepPointer = const[1][1] = address of symbol's string represtation
            const[1][1] = newBucketAddr           # const[1][1] = address of the new bucket, which will contain the string representation
            nextBucketAddr = newBucketAddr + 2
            symbolName = const[2]
            bucketLinkedList.append( [stringRepPointer , nextBucketAddr , symbolName , newBucketAddr] )
            newBucketAddr = nextBucketAddr                
    # last bucket nextBucketAddr - has T_NIL always, to know its over!
    if len(bucketLinkedList) > 0:
        nilAddr = constantTable[ constantLookup["()"] ][0]          # get T_NIL singleton address from constant table        
        bucketLinkedList[len(bucketLinkedList)-1][1] = nilAddr      # change last bucket nextBucketAddr to point to nil constant
    freeAddress = newBucketAddr                                 # update last free address
    
    
def genBucketLinkedList():
    global bucketLinkedList
    global constantTable
    global constantLookup
    code = " /* bucket table init: */\n"
    # bucket list start - 1 size pointer to first bucket (if there is none, then to nil)
    code = code + "        //bucket list start - size 1, pointer to first bucket (if there is none, then to nil constant) \n"
    code = code + """        PUSH(IMM(1)); 
        CALL(MALLOC);
        DROP(1);       \n"""
    for bucket in bucketLinkedList:
        ### TO THINK MAYBE MOVING IT TO AN ASM FUNCTION, WHO ADD A NODE TO THE LIST - SINCE WE NEED TO DO THE SAME INCASE OF STR->SYM
        code = code + "\t    MOV(IND(R0),IMM(" + str(bucket[3]) + "));    \t\t// [R0] = next link pointer = next link address \n"         
        code = code + "\t\t//bucket of symbol: " + str(bucket[2]) + ",\t at address: "+ str(bucket[3]) + "\n" 
        code = code + """        PUSH(IMM(2)); 
        CALL(MALLOC);
        DROP(1);
        MOV(IND(R0),IMM(""" + str(bucket[0]) + """));    \t\t// R0[0] = symbol's stringRep pointer
        INCR(R0);        \t\t\t\t// R0 = second cell in link = R0[1] = next link pointer \n"""
    # last next link pointer - has T_NIL always, to know its over!!!! :  
    nilAddr = constantTable[ constantLookup["()"] ][0]          # get T_NIL singleton address from constant table
    code = code + "\t\tMOV(IND(R0),IMM(" + str(nilAddr) + "));    \t\t// last next link pointer - always point to T_NIL , to recognize list is over \n"
    code = code + " /* bucket table init finished */\n"
    return code            


def genWriteAstResult():
    labelNum = tag_parser.getNewLabelNumber()
    code = "\n\t\t/* write command result: */ \n"
    code = code + """        CMP(INDD(R0,0),IMM(T_VOID));
        JUMP_EQ(CODEGEN_FINISH_""" + str(labelNum) + "); \n"                           # dont print if result is only a void (also true incase of define/display, everthing with no return value
    code = code + """        PUSH(R0);    \t\t// 1 param = R0 = result
        CALL(WRITE_SOB);                      // writing result 
        DROP(1); \n"""
    code = code + """        CALL(NEWLINE);
        /* write result finished */ \n"""

    code = code + "CODEGEN_FINISH_""" + str(labelNum) + ": \n"   
    return code

  
def generate_codegen_skeleton(body):
    code =      """/**/
    #include <stdio.h>
    #include <stdlib.h>
    #include \"cisc.h\"
  
    \n\n    
    int main()
    {
      int i;
      int j;   
      START_MACHINE;
      JUMP(CONTINUE);
        #include \"char.lib\"
        #include \"io.lib\"
        #include \"math.lib\"
        #include \"string.lib\"
        #include \"system.lib\"
        #include \"scheme.lib\"
        #include \"runtime.lib\"
        \n\n""" + genPrintMemProcedure() + "\n\n\n" + genString2SymbolGlobalProcedure() + """\n\n\n
     CONTINUE:
      \n\n"""              ##############################################TO PAY ATTENTION IF I CAN DO IT BETTER WITH i , j!!!!!!!!!!!!!!!!!!!!!!!!!!!
  
    code = code + body
  
    code = code + """\n
     STOP_MACHINE;
     return 0; 
     }"""
    return code              
    
    
def compile_scheme_file(source, target):
    global constantLookup
    global constantTable
    global globalVarTable
    global freeAddress
    if target[-4:]!=".asm":
    	print("WARNING, target file parameter given with wrong suffix, changing to " + target + ".asm")
    	target=target+".asm" 
    sourceFile = open(source , "r")
    sourceCode = sourceFile.read()
    sourceFile.close()
    
    ############################################################
    #adding of YAG PROCEDURE - remember to highlight at the END!!!!!!!!!!!!:
    sourceCode = yagProcedure + sourceCode
    #############################################################
    
    targetCode = ""
    
    #incase we need to eat the whole code first as a group of ast's:
    codeAsts = []
    while(sourceCode!=""):
        (ast,sourceCode) = tag_parser.AbstractSchemeExpr.parse(sourceCode)              # parse each command
        ast = ast.semantic_analysis()                                                   # run semantic ananlysis on each parsing ast
        codeAsts.append(ast)
  
    buildConstantTable(codeAsts)
    buildBucketLinkedList()
    
    targetCode = targetCode + genConstantTable()                                        # gen constant table
    targetCode = targetCode + genBucketLinkedList()                                     # gen symbols buckets linked list
    targetCode = targetCode + buildNgenGlobalVarTable(codeAsts)                         # gen (first also build) global variables table
    
    targetCode = targetCode + "\n\n\t\t// next free address = " + str(freeAddress) +"\n"
    
    ###TO ADD "built-in" procedures TO TARGET (!!!) CODE HERE!!!!!!
    
    for ast in codeAsts:
        targetCode=targetCode + "\n\n/*///////////////////////////////// CODEGEN " + str(ast) + "///////////////////////////////*/\n\n" 
        targetCode=targetCode + ast.code_gen(constantTable,constantLookup,0,globalVarTable)            # gen code for whole command
        targetCode=targetCode + genWriteAstResult() +  "\n"                             # gen writing/returning whole command result 
    
    ################# debug ##################
    #functionality - printing memory at the end of the program, to see what was build during
    #targetCode = targetCode + genMemArrayPrinting()
    #########################################
          
    targetCode = generate_codegen_skeleton(targetCode)
    targetFile = open(target , "w")
    #NOT SURE IF THE WRITING SHOULD BE DONE THIS WAY ACCORDING TO MYER WISHES IN THE ASSIGNMENT PAGE:
    targetFile.write(targetCode)
    targetFile.close()
    
    #initAllDatabases()
